-module(test1_game).

% enable native compiler
-compile([debug_info, {hipe,o3}]).

-export([game1/0, game2/0, game3/0]).

-export([field_new/0, field_move2/2, field_get/3, visual_to_moves/1,
    simple_move_choose/3, known_multi_rules/0, field_win/2]).

-define(ROW,5).
-define(FIELD_BORDER,5).

-record(figure_experience, {exec=orddict:new(), possible=orddict:new()}).
-record(figure_rules, {figure, rules}).



point_max_dist( {X1,Y1}, {X2,Y2} ) ->
    erlang:max( erlang:abs(X1-X2), erlang:abs(Y1-Y2) ).

field_new() -> [].

%field_move(Field, Mark,X,Y) -> [{Mark,X,Y} | Field].

field_move2(Field, {x,{XX,XY}}) -> field_move2(Field, {x,XX,XY});
field_move2(Field, {x,XX,XY}) -> [ {x,XX,XY} | Field ];
field_move2(Field, {x,{XX,XY},o,{OX,OY}}) -> field_move2(Field, {x,XX,XY,o,OX,OY});
field_move2(Field, {x,XX,XY,o,OX,OY}) -> [ {o,OX,OY}, {x,XX,XY} | Field ].

field_get([], _X, _Y) -> free;
field_get( [ {Mark,X,Y} | _Field ], X, Y) -> Mark;
field_get( [ _ | Field ], X, Y) -> field_get(Field,X,Y).

field_has_row(Field, X,Y,Mark) ->
    lists:any( fun(Dir) -> field_has_row(Field, X,Y,Mark,Dir) end, [ {1,1},{-1,1},{1,0},{0,1} ] ).
    % lists:any(Pred, List) -> bool()

field_has_row(Field, X,Y,Mark,{Dx,Dy}) ->
    Left = field_row_track(Field, X,Y,Mark,{Dx,Dy}),
    Right = field_row_track(Field, X,Y,Mark,{-Dx,-Dy}),
    point_max_dist(Left,Right)>?ROW.

field_row_track(Field, X,Y,Mark,{Dx,Dy}) ->
    case field_get(Field, X,Y) of
        Mark -> field_row_track(Field, X+Dx, Y+Dy, Mark, {Dx,Dy});
        _ -> {X,Y}
    end.

field_win(Field, Mark) ->
    Check = fun ({AMark,X,Y}) when AMark =:= Mark -> field_has_row(Field, X,Y,Mark); (_) -> false end,
    lists:any( Check, Field).

field_frees(Field) ->
    Minx = lists:min( [ X || {_Mark,X,_} <- Field ] ),
    Miny = lists:min( [ Y || {_Mark,_,Y} <- Field ] ),
    Maxx = lists:max( [ X || {_Mark,X,_} <- Field ] ),
    Maxy = lists:max( [ Y || {_Mark,_,Y} <- Field ] ),
    [ {X,Y} ||
        X <- lists:seq( Minx - ?FIELD_BORDER, Maxx + ?FIELD_BORDER),
        Y <- lists:seq( Miny - ?FIELD_BORDER, Maxy + ?FIELD_BORDER),
        field_get(Field,X,Y) =:= free ].

figure_flips(Figure) ->
    Flips = [
        fun({X,Y}) -> {X,Y} end,
        fun({X,Y}) -> {-X,Y} end,
        fun({X,Y}) -> {X,-Y} end,
        fun({X,Y}) -> {-X,-Y} end,

        fun({X,Y}) -> {Y,X} end,
        fun({X,Y}) -> {Y,-X} end,
        fun({X,Y}) -> {-Y,X} end,
        fun({X,Y}) -> {-Y,-X} end
    ],
    FlipIt = fun(F,Fig) -> [ F(P) || P <- Fig ] end,
    All = [ FlipIt(F,Figure) || F <- Flips ],
    lists:usort(All).

figure_display(Figure) ->
    Minx = lists:min( [ X || {X,_} <- Figure ] ),
    Miny = lists:min( [ Y || {_,Y} <- Figure ] ),
    Maxx = lists:max( [ X || {X,_} <- Figure ] ),
    Maxy = lists:max( [ Y || {_,Y} <- Figure ] ),
    FSet = gb_sets:from_list(Figure),
    lists:foreach( fun(Y) ->
        Line = [ case gb_sets:is_member({X,Y},FSet) of true -> $*; false -> $\ end ||  X <- lists:seq(Minx,Maxx) ],
        io:format("~s~n", [ Line ] )
    end, lists:seq(Miny,Maxy)).


%% a - active (my), o - other player, f - free
field_figure_bind(Field, Figure, X,Y, ActiveMark) ->
    Res = [ case field_get(Field, X+XF,Y+YF) of free -> $f; ActiveMark -> $a; _ -> $o end || {XF,YF} <- Figure ],
    %case Res of "aaa" -> io:format("XY aaa ~w~n",[{X,Y}]); _ -> ok end,
    Res.

field_figure_bind_flips(Field, Figure, X,Y, ActiveMark) ->
    FigFlips = figure_flips(Figure),
    [ field_figure_bind(Field,F, X,Y,ActiveMark) || F <- FigFlips ].

field_figure_bind_flips_update_grp(Grps, Field, Figure, X,Y, ActiveMark) ->
        F=fun(Bind,IGrps) ->
            %if
            %    Bind =:= "ooo" -> io:format("Bind~p coord ~p~n",[Bind,{X,Y}]);
            %    true -> ok
            %end,
            orddict:update_counter(Bind,1,IGrps)
        end,
        lists:foldl( F , Grps,
            field_figure_bind_flips(Field, Figure, X,Y,ActiveMark)  ).

field_free_scan(BaseCounts, Field, Figure, ActiveMark, ExcludeXY) ->
    lists:foldl( fun({X,Y},Counts) ->
        field_figure_bind_flips_update_grp(Counts, Field, Figure, X,Y,ActiveMark)
    end, BaseCounts, lists:delete(ExcludeXY, field_frees(Field)) ).

%orddict_increment(To, [] ) -> To;
%orddict_increment(To, [From|Tail] ) -> orddict_increment( orddict:update_counter(From, 1, To), Tail ).

figure_experience_update(#figure_experience{exec=Exec, possible=Possible}, Field, Figure, ActiveMark, ExecPoint) ->
    NewPossibleCounts = field_free_scan(Possible, Field,Figure,ActiveMark,ExecPoint),
    {X,Y} = ExecPoint,
    NewExecCounts = field_figure_bind_flips_update_grp(Exec, Field, Figure, X,Y,ActiveMark),
    #figure_experience{exec=NewExecCounts, possible=NewPossibleCounts}.

game_expand(GameMoves, Move2, Field) ->
    {x,XX,XY,o,OX,OY} = Move2,
    FMoveA = [ {x,XX,XY} | Field ],
    [ {{o,OX,OY}, FMoveA}, { {x,XX,XY}, Field } | GameMoves ].

game_expand_all(Moves) ->
    {GameMoves,_LastField} = lists:foldl( fun(Move2, State) ->
        %io:format("game_expand_all state ~p~n",[State]),
        %io:format("0"),
        {GameMoves,F0} = State,
        %io:format("1"),
        NewGM = game_expand(GameMoves, Move2, F0),
        %io:format("2 NewGM=~p~n",[NewGM]),
        [ {M,F} | _Older ] = NewGM,
        %io:format("3"),
        {NewGM, [M|F] }
    end, {field_new(),[]}, Moves ),
    lists:reverse(GameMoves).

orddict_find_def(Key, Def, Orddict) ->
    case orddict:find(Key, Orddict) of
        {ok, Value} -> Value;
        error -> Def
    end.

figure_rules(#figure_experience{ exec=Exec, possible=Possible }) ->
    [ {State,Count,P,Count/(Count+P) } || {State,Count} <- Exec, P <- [orddict_find_def(State,0,Possible)] ].

field_from_moves(Moves) ->
    lists:foldl( fun(Move2, F) -> field_move2(F, Move2) end, field_new(), Moves ).

keyfind_as_list(Key, N, TupleList) ->
    case lists:keyfind(Key,N,TupleList) of
        false -> [];
        Tuple -> [Tuple]
    end.

figure_rules_max_val(#figure_rules{figure=F, rules=Rules}, Field, {X,Y}, ActiveMark) ->
    Found = [ {Koef,Pat,F} ||
        B <- field_figure_bind_flips(Field, F, X,Y, ActiveMark), _R={Pat,_,_,Koef} <- keyfind_as_list(B,1,Rules) ],
    case Found of
        [] -> {-1,no_pat,no_fig};
        _ -> lists:max(Found)
    end.

%multi_figure_rules_max_val( MultiFigureRules, Field, {X,Y}, ActiveMark) ->
%    lists:max( [ {_Koef,_Pat} = figure_rules_max_val(R, Field, {X,Y}, ActiveMark) || R <- MultiFigureRules ] ).

figure_rules_best_move(FR=#figure_rules{}, Field, ActiveMark) ->
    FAssess=fun({X,Y}) ->
        BestHere = figure_rules_max_val(FR, Field, {X,Y}, ActiveMark),
        {BestHere,{X,Y}}
        %io:format("~p ~p~n",[{X,Y}, BestHere])
    end,
    lists:max(lists:map(FAssess, field_frees( Field ))).

multi_figure_rules_best_move(MultiFigureRules, Field, ActiveMark) ->
    lists:max( [ figure_rules_best_move(R,Field,ActiveMark) || R <- MultiFigureRules ] ).


game_experience_update(PrevExp = #figure_experience{}, Moves, Figure) ->
    Winner = moves_winner(Moves),
    GameMoves = game_expand_all(Moves),
    _FigureExper = lists:foldl( fun({Move,StartField}, Exp) ->
        case Move of
            {Winner,X,Y} ->
                figure_experience_update(Exp, StartField, Figure, Winner, {X,Y});
            _ -> Exp
        end
     end, PrevExp, GameMoves).

visual_to_moves(Visual) ->
    All = visual_all_coords(Visual),
    %io:format("All ~p~n",[All]),

    Mapper = fun({[{Xa,Ya}],[{Xb,Yb}]}) -> {x,Xa,Ya,o,Xb,Yb}; ({[{Xa,Ya}],[]}) -> {x,Xa,Ya} end,

    [ Mapper(Pair) || Pair = {[{_Xa,_Ya}],_MoveO} <- All ].

%[{[{1,1}],[{2,1}]},
% {[{2,3}],[{3,1}]},
% {[{3,3}],[{4,1}]},
% {[{3,2}],[{5,1}]},
% {[{2,2}],[{6,1}]},...

visual_all_coords(Visual) ->
    [ {visual_coords(Visual,string:to_upper(L)), visual_coords(Visual,L) }  || L <- lists:seq($a,$z) ].

visual_coords(Visual, Char) ->
    Lines = string:tokens(Visual,"\r\n"),
    [ {string:chr(S,Char),N} || {S,N} <- lists:zip(Lines, lists:seq(1,length(Lines))), string:chr(S,Char)>0  ].

moves_winner(Moves) ->
    Finish = lists:foldl( fun(Move2, F) -> field_move2(F, Move2) end, field_new(), Moves ),
    Wx = field_win(Finish, x),
    Wo = field_win(Finish, o),
    if
        Wx andalso (not Wo) -> x;
        Wo andalso (not Wx) -> o
    end.


game_experience_collect(Games, Fig) ->
    lists:foldl(fun (Moves,E) -> game_experience_update(E, Moves, Fig) end, #figure_experience{}, Games ).

known_games() ->
    Res = test1_game_data:known_games(),
    lists:foreach(fun(M) -> moves_winner(M) end, Res), % verify every example has a winner
    Res.

known_multi_rules() ->
    %Figs = [ [{0,1},{0,2},{0,3}], [{0,1},{0,2},{0,3},{0,4}], [{0,1},{0,2},{0,3},{0,4},{0,5}],
    %        [{1,1},{2,2},{3,3}], [{1,1},{2,2},{3,3},{4,4}], [{1,1},{2,2},{3,3},{4,4},{5,5}] ],
    Figs = test1_game_data:known_figures(),
    [ #figure_rules{figure=F, rules=figure_rules(game_experience_collect(known_games(), F))} || F <- Figs ].


simple_move_choose(KnownMultiRules, Field, ActiveMark) ->
    {_Rule,Move}=BestMove = multi_figure_rules_best_move( KnownMultiRules , Field, ActiveMark ),
    io:format("BestMove ~p~n",[BestMove]),
    Move.

game3() ->
    Moves = visual_to_moves("
Aab
 B
  C
    "),
   io:format("Moves: ~p~n",[Moves]),
   FieldNew = field_from_moves(Moves),
   io:format("MRules: ~p~n",[known_multi_rules()]),
    {_Rule,_Move}=BestMove = multi_figure_rules_best_move( known_multi_rules() , FieldNew, o ),
    io:format("BestMove ~p~n",[BestMove]).

game2() ->
    Fig = [{0,1},{0,2},{0,3}],
    %E1 = game_experience_update(#figure_experience{}, Moves1, Fig),
    %FigureExper = game_experience_update(E1, Moves2, Fig),
    FigureExper = game_experience_collect(known_games(), Fig),

     Rules = figure_rules(FigureExper),
     io:format("Rules: ~p~n",[Rules]),
     MovesNew =
     [{x,1,1,o,2,1},
      {x,2,3,o,3,1},
      {x,3,3} ],
     FieldNew = field_from_moves(MovesNew),
     BestMove = figure_rules_best_move(#figure_rules{figure=Fig, rules=Rules}, FieldNew, o),
     io:format("Best move is ~p for field ~p~n",[BestMove,FieldNew]).



game1() ->
    Fig = [{0,1},{0,2},{0,3}],
    figure_display(Fig),
    FigFlips = figure_flips(Fig),
    FigFlipsN = lists:zip(FigFlips,lists:seq(1,length(FigFlips))),
    lists:foreach( fun({F,N}) -> io:format("~p  ~p~n",[N,F]), figure_display(F) end, FigFlipsN ),
    Moves =
    [ {x,1,1,o,2,1},
      {x,2,3,o,3,1},
      {x,3,3,o,4,1},
      {x,3,2,o,5,1},
      {x,2,2,o,6,1} ],
    Finish = lists:foldl( fun(Move2, F) -> field_move2(F, Move2) end, field_new(), Moves ),
    io:format("Binds: ~p~n", [ field_figure_bind_flips(Finish, Fig, 6,1,o) ] ),

    io:format("Free binds: ~p~n", [orddict:to_list( field_free_scan(orddict:new(), Finish,Fig,o,{6,1}) )]),

    io:format("x win ~w~n", [field_win(Finish, x)]),
    io:format("o win ~w~n", [field_win(Finish, o)]),

    GameMoves = game_expand_all(Moves),
    io:format("o game-all ~p~n", [GameMoves]),

    Winner = o,
    FigureExper = lists:foldl( fun({Move,StartField}, Exp) ->
        case Move of
            {Winner,X,Y} ->
                figure_experience_update(Exp, StartField, Fig, Winner, {X,Y});
            _ -> Exp
        end
     end, #figure_experience{}, GameMoves),
     Rules = figure_rules(FigureExper),
     io:format("Rules: ~p~n",[Rules]),
     MovesNew =
     [{x,1,1,o,2,1},
      {x,2,3,o,3,1},
      {x,3,3} ],
     FieldNew = field_from_moves(MovesNew),
     BestMove = figure_rules_best_move(#figure_rules{figure=Fig, rules=Rules}, FieldNew, o),
     io:format("Best move is ~p for field ~p~n",[BestMove,FieldNew]).
    %field_frees( FieldNew ),

    %field_figure_bind_flips(FieldNew, Fig, 4,1, o)
    %figure_rules_max_val(#figure_rules{figure=Fig, rules=Rules}, FieldNew, {4,1}, o).

%      > lists:foldl(fun(X, Sum) -> X + Sum end, 0, [1,2,3,4,5]).
%      15
